package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

var users *mongo.Collection
var client *mongo.Client

func init() {
	// var err error
	// clientOpt := options.Client()
	// // loggerOpt := options.Logger()
	// // loggerOpt.SetComponentLevel(options.LogComponentCommand, options.LogLevelDebug)
	// // clientOpt.ApplyURI(uri).SetLoggerOptions(loggerOpt)

	// uri := "mongodb://localhost:27017/"
	// clientOpt.ApplyURI(uri)
	// client, err = mongo.Connect(context.TODO(), clientOpt)
	// if err != nil {
	// 	log.Fatal(err)
	// }

	// err = client.Ping(context.TODO(), nil)
	// if err != nil {
	// 	log.Fatal(err, "####")
	// }
	// fmt.Println(client)

	// db := client.Database("test")
	// users = db.Collection("users")

}

func InsertOne() {
	tom := User{Name: "tom", Age: 18}
	ior, err := users.InsertOne(context.TODO(), tom)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(ior.InsertedID)
}
func InsertMany() {
	ben := User{Name: "ben", Age: 26}
	jerry := User{Name: "jerry", Age: 35}
	imr, err := users.InsertMany(context.TODO(), []interface{}{ben, jerry})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(imr.InsertedIDs...)
}

func InsertOne2() {
	tom := User{Name: "sam", Age: 18}
	ior, err := users.InsertOne(context.TODO(), tom)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(ior.InsertedID)
}

func FindOne() {
	filter := bson.D{}
	sr := users.FindOne(context.TODO(), filter)
	fmt.Println(sr.Raw())
	fmt.Println(sr.Err())

	// var i any

	// err := sr.Decode(&i)
	// fmt.Println(err, "#####")
	// fmt.Printf("%T %[1]v\n", i)
	var u User

	err := sr.Decode(&u)
	fmt.Println(err, "#####")
	fmt.Printf("%T %[1]v\n", u)
}

func FindMany() {
	filter := bson.D{}
	cursor, err := users.Find(context.TODO(), filter)
	if err != nil {
		log.Fatal(err)
	}

	defer cursor.Close(context.TODO())
	var us []*User
	for cursor.Next(context.TODO()) {
		var u User
		err := cursor.Decode(&u)
		if err != nil {
			continue
		}
		us = append(us, &u)
	}
	fmt.Println(us)

}

// func Many2() {
// 	filter := bson.D{}
// 	cursors, err := users.Find(context.TODO(), filter)
// 	defer cursors.Close(context.TODO())
// 	if err != nil {
// 		log.Fatal(err)
// 	}
// 	for cursors.Next(context.TODO()) {

//			fmt.Println(cursors.Current)
//		}
//	}
//
//	func Many2() {
//		filter := bson.D{}
//		cursors, err := users.Find(context.TODO(), filter)
//		defer cursors.Close(context.TODO())
//		if err != nil {
//			log.Fatal(err)
//		}
//		for cursors.Next(context.TODO()) {
//			var u User
//			//fmt.Println(cursors.Current)
//			err := cursors.Decode(&u)
//			if err != nil {
//				continue
//			}
//			fmt.Println(u)
//		}
//	}

// func Many2() {
// 	filter := bson.D{}
// 	cursors, err := users.Find(context.TODO(), filter)
// 	defer cursors.Close(context.TODO())
// 	if err != nil {
// 		log.Fatal(err)
// 	}
// 	var us []*User

// 	for cursors.Next(context.TODO()) {
// 		var u User
// 		//fmt.Println(cursors.Current)
// 		err := cursors.Decode(&u)
// 		if err != nil {
// 			continue
// 		}
// 		//fmt.Println(u)
// 		us = append(us, &u)
// 	}
// 	fmt.Println(us)

// }
func (u User) String() string {

	return fmt.Sprintf("<User %v %s %d>", u.Id.Hex()[12:24], u.Name, u.Age)

}

func FindMany2() {
	//filter := bson.D{}
	FindByFilter()

}
func FindByFilter() {
	filter := bson.D{}
	// filter := bson.D{}
	// filter = bson.D{{"name", "sam"}}
	// filter = bson.D{{"name", bson.D{bson.E{"$eq", "tom"}}}}

	// filter := bson.M{}
	// filter = bson.M{"name": "sam"}
	// filter = bson.M{"name": bson.M{"$eq": "tom"}}
	// filter = bson.M{"age": bson.M{"$gt": 19}}
	filter = bson.D{{"age", bson.D{{"$gt", 25}}}}
	FindAll(filter, nil)
}
func FindAll(filter any, opt *options.FindOptions) {
	//filter := bson.D{}
	cursor, err := users.Find(context.TODO(), filter, opt)
	defer cursor.Close(context.TODO())
	if err != nil {
		log.Fatal(err)
	}
	var us []*User

	if cursor.All(context.TODO(), &us); err != nil {
		log.Fatal(err)
	}

	for _, u := range us {
		fmt.Println(u)

	}

}
func UpdateOne(filter bson.M, value bson.M) {
	//id = bson.M{}
	//value = bson.M{}
	result, err := users.UpdateOne(context.TODO(), filter, value)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(result.UpsertedCount, result.ModifiedCount, result.MatchedCount)

}

func UpdateMany(filter bson.M, value bson.M) {

	results, err := users.UpdateMany(context.TODO(), filter, value)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(results.UpsertedID, results.MatchedCount, results.ModifiedCount, results.UpsertedCount)

}

func Replace() {

	result, _ := users.ReplaceOne(context.TODO(), bson.M{"name": "sam"}, bson.M{"score": 88})
	fmt.Println(result.UpsertedID, result.MatchedCount, result.ModifiedCount)

}
func DeleteMany() {
	// result, err := users.DeleteMany(context.TODO(), bson.M{
	// 	"$exists": bson.M{"score": true},
	// })
	result, err := users.DeleteMany(context.TODO(), bson.M{
		"score": bson.M{"$exists": true},
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(result.DeletedCount)
}
func DeleteOne() {

	id, _ := primitive.ObjectIDFromHex("000000000000000000000000")
	result, _ := users.DeleteOne(context.TODO(), bson.M{"_id": id})
	fmt.Println(result.DeletedCount)
}
func main() {
	// defer func() {
	// 	if err := client.Disconnect(context.TODO()); err != nil {
	// 		log.Fatal(err)
	// 	}
	// }()
	//InsertOne()
	//InsertMany()
	//InsertOne2()
	//FindOne()
	//FindMany()
	//FindMany2()

	//FindByFilter(filter)
	//filter := bson.M{}
	// opt := options.Find()
	// opt.SetProjection(bson.M{"name": true, "age": true})
	// opt.SetSort(bson.M{"name": -1}).SetLimit(3).SetSkip(2)
	// FindAll(filter, opt)
	// { $set: { <field1>: <value1>, ... } }
	// id := bson.M{"_id": "000000000000000000000000"}
	//id, _ := primitive.ObjectIDFromHex("000000000000000000000000")
	//fmt.Printf("id %s\n", id)
	// result, err := users.UpdateByID(context.TODO(), id, bson.M{"$set": bson.M{"name": "johnson"}})
	// if err != nil {
	// 	log.Fatal(err)
	// }
	// fmt.Println(result.MatchedCount, result.UpsertedID)
	//filter = bson.M{"_id": id}
	//filter = bson.M{"name": "ben"}

	// value := bson.M{"name": "john"}
	// value := bson.M{"$set": bson.M{"name": "john"}}
	// value := bson.M{"$inc": bson.M{"age": -5}}
	//value := bson.M{"$set": bson.M{"name": "benson"}}
	//UpdateOne(filter, value)
	//UpdateMany(filter, value)
	//	Replace()
	// DeleteOne()
	// DeleteMany()
	parent := context.Background()
	// ctx, cancel := context.WithTimeout(parent, 10*time.Second)
	// defer cancel()

	// go func(c context.Context) {
	// 	tick := time.NewTicker(time.Second)
	// 	for {
	// 		select {
	// 		case <-tick.C: // 工作通道，有数据我们做事情
	// 			fmt.Println("每秒做一次工作内容")
	// 		case <-c.Done():
	// 			fmt.Println("收到了父协程中ctx的结束信号, 我子协程退出")
	// 			return // 当前协程结束了
	// 		}
	// 	}
	// }(ctx)

	// fmt.Println("~~~~~~~~~~~~~~~~~~~~~~~~~~~")
	// select {
	// case <-ctx.Done():
	// 	time.Sleep(2)
	// 	fmt.Println("主协程即将结束了")
	// }
	// fmt.Println("--------------------------")

	timeoutCtx, cancel := context.WithTimeout(parent, 2*time.Second)
	defer cancel()
	var err error
	clientOpt := options.Client()

	uri := "mongodb://localhost:27017/"
	clientOpt.ApplyURI(uri)
	client, err = mongo.Connect(context.TODO(), clientOpt)
	if err != nil {
		log.Fatal(err)
	}

	// err = client.Ping(context.TODO(), nil)
	err = client.Ping(timeoutCtx, nil)
	if err != nil {
		fmt.Println(timeoutCtx.Err().Error())
		fmt.Println(timeoutCtx.Err() == context.DeadlineExceeded)
		fmt.Printf("%T\n", err)
		log.Fatal(err, "####")
	}
	fmt.Println(client)

	//db := client.Database("test")
	//users = db.Collection("users")
}

type User struct {
	// 主键，没有提供的话，生成一个主键id
	Id   primitive.ObjectID `bson:"_id,omitempty"` // [12]byte
	Name string
	Age  int
}
